package qj.concurrent;

import java.util.*;

/**
 * The ServantManager possess a thread pool, here each thread stands for a concurrent.
 * By calling to doServe, the user will assign a new task to the ServantManager.
 * Then, ServantManager will choose a free thread, wake it up and make it finish the job.
 * Implement TaskProgressListenable and addTaskProgressListener in order to know the progress of the task.
 *
 * call to doServe, the manager will assign a thread to finish the job, guarranty that the task will be done
 * Created by QuanLA
 * Date: Mar 14, 2006
 * Time: 2:33:29 PM
 */
public class ServantManager {
    private final ServantThreadPool pool = new ServantThreadPool(this);
    private final List pendingTasks = Collections.synchronizedList(new LinkedList());

    private final List processingTaskPackages = Collections.synchronizedList(new ArrayList());

    /**
     * setMaxThread(0) if no restriction on number of threads.
     * @param maxThreadNum
     */
    public void setMaxThread(int maxThreadNum) {
        pool.setRestrictSize(maxThreadNum);
    }

    /**
     * Request believe that the task will be done
     * @param task
     */
    public void doServe(ServantTask task) {
        ServantThread thread = (ServantThread) pool.acquire();
        if (thread==null)
            pendingTasks.add(task);
        else {
            synchronized(thread) {
                thread.setTask(task);

                // Wake up and work
                thread.notify();
            }
        }
    }

    /**
     * Prior to calling this method: create a collection of ServantTask.
     * The calling thread will wait until all tasks are done.
     * @param taskPakage: Collection of ServantTask
     */
    public void doServeTaskPackage(Collection taskPakage) {
        if (taskPakage==null || taskPakage.size()==0)
            return;

        // Add to processingTaskPackages
        TaskPackageHanger hanger = new TaskPackageHanger(taskPakage);
        processingTaskPackages.add(hanger);
        for (Iterator iterator = taskPakage.iterator(); iterator.hasNext();) {
            ServantTask task = (ServantTask) iterator.next();
            doServe(task);
        }

        if (processingTaskPackages.contains(hanger)) {
            try {
                synchronized (hanger) {
                    hanger.wait();
                }
            } catch (InterruptedException e) {
            }
        }
    }

    /**
     * Call by the ServantThread when finish task
     * Check if pending task left?
     *  Go on with new task,
     * @param thread
     */
    public void doneTask(ServantThread thread) {
        // Go through processingTaskPackages
        // With each collection:
        // If param task == element>finish++
        // If finish>= size: notify col remove from processingTaskPackages list
        synchronized(processingTaskPackages) {
            for (int i = processingTaskPackages.size()-1; i > -1 ; i--) {
                TaskPackageHanger taskPackageHanger = (TaskPackageHanger) processingTaskPackages.get(i);
                for (Iterator iterator = taskPackageHanger.taskPackage.iterator(); iterator.hasNext();) {
                    ServantTask task = (ServantTask) iterator.next();
                    if (task==thread.getCurrentTask()) {
                        taskPackageHanger.finished++;
                    }
                }

                if (taskPackageHanger.finished>=taskPackageHanger.size) {
                    synchronized (taskPackageHanger) {
                        taskPackageHanger.notifyAll();
                    }
                    processingTaskPackages.remove(i);
                }
            }
        }

        // Check if pending task left?
        //  Go on with new task,
        if (pendingTasks.size()==0) {
            pool.release(thread);
            thread.setTask(null);
        } else {
            thread.setTask((ServantTask) pendingTasks.remove(0));
        }
    }


    /**
     * Hold a task collection and its detail
     */
    private static class TaskPackageHanger {
        Collection taskPackage;
        int size;
        int finished;

        public TaskPackageHanger(Collection taskPackage) {
            this.taskPackage = taskPackage;
            size = taskPackage.size();
        }
    }
}